/**
 * This class visualizes the Twitter feed.
 */
package org.hackystat.ui.hackerrific.client;

import java.util.ArrayList;
import java.util.ListIterator;

import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * @author David J. Nickles
 *
 */
public class StatusesPanel extends VerticalPanel {

  /** {@literal} WAIT_IMAGE icon. */        
  public static final String WAIT_IMAGE = "images/wait-animated.gif"; 
  /** {@literal} SMALLLABELSTYLE stylesheet class for labels. */  
  private static final String SMALLLABELSTYLE = "label_sm";   
  /** {@literal} SMALLLABELSTYLE stylesheet class for examples. */  
  private static final String EXAMPLELABELSTYLE = "label_example"; 
  /** {@literal} VALIDLABELSTYLE stylesheet class for valid results. */  
  private static final String VALIDLABELSTYLE = "label_valid"; 
  /** {@literal} INVALIDLABELSTYLE stylesheet class for invalid results. */  
  private static final String INVALIDLABELSTYLE = "label_invalid";
  /** {@literal} EVENSTATUSPANELSTYLE stylesheet class for examples. */  
  private static final String EVENSTATUSPANELSTYLE = "status-even"; 
  /** {@literal} ODDSTATUSPANELSTYLE stylesheet class for examples. */  
  private static final String ODDSTATUSPANELSTYLE = "status-odd"; 
  /** {@literal} DEFAULT_POLLING_INTERVAL default polling interval in seconds. */  
  private static final int DEFAULT_POLLING_INTERVAL = 1; 
  /** {@literal} PRCERRORNAME name for RPC errors. */  
  private static final String PRCERRORNAME = "RPC client error"; 
  
  private Boolean rpcOpen;
  private PopupPanel waitPanel;
  private String lastTweetId;
  private String lastTweetTimestamp;
  private Timer poller;
  
  /**
   * This method sets up the timer task.
   */
  public StatusesPanel() {
    this.waitPanel = new PopupPanel();
    this.waitPanel.add(new Image(WAIT_IMAGE));
    this.rpcOpen = Boolean.TRUE;
    this.lastTweetId = null;
    this.lastTweetTimestamp = null;
    initTimer(); // NOPMD
  }
  
  /**
   * Resets the statuses panel.
   */
  public void reset() {
    this.lastTweetId = null;
    this.lastTweetTimestamp = null;
    clear();
    poll();
  }
  
  /**
   * This method restarts the timer task with the specified interval. 
   */
  private void initTimer() {
    int pollingInterval = 0;
    String refreshCookie = Cookies.getCookie("refresh");
    if (refreshCookie == null) {
      pollingInterval = DEFAULT_POLLING_INTERVAL;
    } 
    else {
      try {
        switch (Integer.parseInt(refreshCookie)) {
          case 0: 
            pollingInterval = 1;
            break;
          case 1: 
            pollingInterval = 5;
            break;
          case 2: 
            pollingInterval = 15;
            break;
          case 3: 
            pollingInterval = 30;
            break;
          case 4: 
            pollingInterval = 60;
            break;
          default: 
            pollingInterval = 1;
            break;
        }
      } 
      catch (NumberFormatException e) {
        pollingInterval = DEFAULT_POLLING_INTERVAL;
      }
    }
    if (this.poller == null) {
      this.poller = new Timer() {
        public void run() {
          poll();
        }
      };
    }
    this.poller.scheduleRepeating(pollingInterval * 60 * 1000);
  }

  /**
   * This method digests the response from the RPC call into local data structures.
   * @param statuses an ArrayList of ArrayLists of Strings containing the tweets.
   * 
   * @gwt.typeArgs statuses <java.util.ArrayList<java.lang.String>> 
   */
  private void update(ArrayList statuses) {
    if (!statuses.isEmpty()) {
      this.lastTweetId = (String)((ArrayList)statuses.get(statuses.size() - 1)).get(1);
      this.lastTweetTimestamp = (String)((ArrayList)statuses.get(statuses.size() - 1)).get(0);
    }
    int tweetCount = this.getWidgetCount();
    for (ListIterator i = statuses.listIterator(); i.hasNext();) {
      ArrayList rawTweet = (ArrayList)i.next();
      StatusPanel tweetPanel = new StatusPanel(rawTweet);
      insert(tweetPanel, 0);
      if (((++tweetCount) % 2) == 0) {
        tweetPanel.addStyleName(EVENSTATUSPANELSTYLE);
      }
      else {
        tweetPanel.addStyleName(ODDSTATUSPANELSTYLE);
      }
    }
    this.waitPanel.hide();
    this.rpcOpen = Boolean.TRUE;
  }

  /**
   * This method performs an asynchronous remote call out to the sensor data viewer service
   * for particular sensor data over a specified period as indicated in the URI.
   * @param user for authentication
   * @param password for authentication
   * @param type the timeline type: public, friends, user
   * @param lastId get entries since the last entry id
   */ 
  private void rpcGet(String user, String password, String type, String lastId) { 
    if (user == null || password == null) {
      log("Error", "user/password cookies not set.");
    }
    else {
      RPCServiceFactory.getInstance().getService().timeline(user, password, type, lastId,
          new AsyncCallback()
          {
            public void onSuccess(Object statuses) {
              /** @gwt.typeArgs statuses <java.util.ArrayList<java.lang.String>> */
              update((ArrayList) statuses);
            }

            public void onFailure(Throwable caught) {
              if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
                log(PRCERRORNAME, "Cannot invoke method on servlet.");
              } 
              else {
                log(PRCERRORNAME, caught.toString());
              }
            }
          });
    }
  }
  
  /**
   * This is the callback method for the Timer task. This method polls the servlet 
   * (which communicates with the Twitter server).
   */
  public final void poll() {
    if (this.rpcOpen.booleanValue() && this.isVisible()) {
      this.rpcOpen = Boolean.FALSE;
      this.waitPanel.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
        public void setPosition(int offsetWidth, int offsetHeight) {
          int left = (Window.getClientWidth() - offsetWidth) / 2;
          int top = (Window.getClientHeight() - offsetHeight) / 4;
          waitPanel.setPopupPosition(left, top);
        }
      });
      String type = null;
      String query = null;
      switch (Integer.parseInt(Cookies.getCookie("source"))) {
        case 0: 
          type = "friends";
          query = this.lastTweetTimestamp;
          break;
        case 1: 
          type = "public"; 
          query = this.lastTweetId;
          break;
        case 2: 
          type = "favorites"; 
          query = null;
          break;
        case 3: 
          type = "direct_messages"; 
          query = null;
          break;
        case 4: 
          type = "user"; 
          query = this.lastTweetTimestamp;
          break;
        default: 
          type = "public"; 
          query = null;
          break;
      }
      rpcGet(Cookies.getCookie("user"), Cookies.getCookie("password"), type, query);
    }
  }
  
  /**
   * Log this message in the SensorDataViewer data display.
   * @param header the message header or subject
   * @param body the message body
   */
  public void log(String header, String body) {
    // write the message to the display
  }
}
